#include "mtpinterface.h"
#include <QMessageBox>

MTPInterface::MTPInterface(XMLInterface *xmlInt, LocalInterface *locInt, QObject *parent) :
    QObject(parent)
{
    xml = xmlInt;
    loc = locInt;
    loc->readLocalXml();

    mtpCopyProc = new QProcess();
    connect(mtpCopyProc, SIGNAL(readyReadStandardOutput()), this, SLOT(mtpCopyProcOutput()));
    QStringList args;
#ifdef Q_WS_WIN
    // assumes mtpinterface.exe is located in the application directory
    args << "/C" <<  QCoreApplication::applicationDirPath() + "/mtpinterface.exe";
    mtpCopyProc->start("C:\\Windows\\system32\\cmd.exe", args);
    connect(mtpCopyProc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(mtpProcError(QProcess::ProcessError)));
#endif
    // TODO: support for non-windows if necessary
    connect(this, SIGNAL(updateDevice()), this, SLOT(pushData()));
    connect(this, SIGNAL(restartMTP()), this, SLOT(restartMTPProc()));
}

void MTPInterface::restartMTPProc()
{
    QStringList args;
#ifdef Q_WS_WIN
    args << "/C" << "mtpinterface.exe";
    mtpCopyProc->start("C:\\Windows\\system32\\cmd.exe", args);
#endif
}

void MTPInterface::mtpProcError(QProcess::ProcessError error)
{
    switch (error)
    {
    case QProcess::FailedToStart:
        emit logMessage("mtp: mtpinterface failed to start. Is it in the same directory as this program?", ERROR);
        break;
    case QProcess::Crashed:
        // TODO: handle this
        emit logMessage("mtp: mtpinterface crashed! Attempting to restart.", ERROR);
        emit restartMTP();
        break;
    case QProcess::Timedout:
        emit logMessage("mtp: mtpinterface timed out. This should not happen", ERROR);
        break;
    case QProcess::WriteError:
        emit logMessage("mtp: Failed to write to stdin of mtpinterface.", ERROR);
        break;
    case QProcess::ReadError:
        emit logMessage("mtp: Failed to read from stdout of mtpinterface. Is it running?", ERROR);
        break;
    case QProcess::UnknownError:
        emit logMessage("mtp: Unknown error with mtpinterface. Proceed with caution!", ERROR);
    }
}

void MTPInterface::closeMtpCopy()
{
    mtpCopyProc->kill();
    /*QByteArray writeLine;
    writeLine.push_back(QByteArray::number(COMMAND));
    writeLine.push_back(' ');
    writeLine.push_back(QByteArray::number(QUIT));
    writeLine.push_back('\n');
    mtpCopyProc->write(writeLine);*/
}

void MTPInterface::mtpCopyProcOutput()
{
    QByteArray output = mtpCopyProc->readAllStandardOutput();
    QList<QByteArray> lines = output.split('\n');
    QByteArray writeLine;
    QMessageBox *msgbox;
    QPushButton *owrite, *keep, *read, *ignore;
    QString fName, fPath, fProp, tmpLine;
    QFile *f;
    QFileInfo *fInfo;
    PropData *tmpProp;
    for (int k = 0; k < lines.size(); k++)
    {
        switch (lines[k][0])
        {
        case '0':
            // info message
            emit logMessage("mtp: " + QString(lines[k].right(lines[k].size()-2)).trimmed(), INFO);
            break;
        case '1':
            // important message
            emit logMessage("mtp: " + QString(lines[k].right(lines[k].size()-2)).trimmed(), IMPORTANT);
            break;
        case '2':
            // error message
            emit logMessage("mtp: " + QString(lines[k].right(lines[k].size()-2)).trimmed(), ERROR);
            break;
        case '3':
            // status
            // TODO: keep track of what mtpinterface is doing.
            //       Lock the interface while copying, release when done copying.
            //       sub cmds:
            //          COPY - copy has started, disable interface
            //          END_COPY - all done, enable interface
            //          END_PROP - possibly use for status updates
            qDebug() << "case 3 " <<  lines[k];
            switch (lines[k][2])
            {
            case '0':
                // COPY
                emit copyInProgress(true);
                break;
            case '1':
                // END_COPY
                emit copyInProgress(false);
                //copyXMLFromDevice();
                break;
            case '2':
                // END_PROP
                break;
            case '4':
                // DEV_DISCONNECT
                writeLine.push_back(QByteArray::number(COMMAND));
                writeLine.push_back(' ');
                writeLine.push_back(QByteArray::number(DEV_DISCONNECT));
                writeLine.push_back('\n');
                mtpCopyProc->write(writeLine);
                // add a sync option instead of save
                // 1. copy new data to device and to some temp folder
                // 2. copy pre-existing data on the device to the same temp folder
                // 3. modify all props so that the paths point to the tmp folder
                //    This will allow the user to disconnect the device, connect another, then sync that new device
                emit deviceConnect(false);
                break;
            case '5':
                // DEV_CONNECT
                // TODO: still load from the device xml? should probably ask to copy (or copy w/o asking).
                // only copy if there are no props already.
                msgbox = new QMessageBox;
                msgbox->setText("New device connected.");
                msgbox->setInformativeText(QString("Do you want to read prop data from the device (recommended if there is no ") +
                                           QString("prop data loaded on this machine) or ignore the data on the device (recommended " +
                                           QString("if there is already prop data on this machine)?\n\nAny data read from the device will " +
                                           QString("replace data on this machine."))));
                read = msgbox->addButton("Read from device", QMessageBox::AcceptRole);
                ignore = msgbox->addButton("Ignore device data", QMessageBox::RejectRole);
                msgbox->exec();
                if ((QPushButton *)msgbox->clickedButton() == read)
                {
                    emit copyInProgress(true);
                    loc->removeAll();
                    xml->removeAllProps();
                    copyAllDataFromDevice(QDir::toNativeSeparators(QDir::homePath() + "/.artour"));
                }
                else if ((QPushButton *)msgbox->clickedButton() == ignore)
                {
                }
                delete msgbox;
                //copyXMLFromDevice();
                //copyAllDataFromDevice(QDir::toNativeSeparators(QDir::homePath() + "/.artour"));
                emit deviceConnect(true);
                break;
            case '6':
                // TMP_XML_PATH
                //emit logMessage("mtp: " + QString(lines[k].right(lines[k].size() - 4)).trimmed(), INFO);
                emit logMessage("mtp: Loading prop data from device", INFO);
                xml->readXML(QString(lines[k].right(lines[k].size() - 4)).trimmed());
                break;
            case '7':
                // FILE_EXISTS
                msgbox = new QMessageBox;
                fName = "";
                tmpLine = QString(lines[k].right(lines[k].size() - 6)).trimmed();
                fProp = tmpLine.left(tmpLine.indexOf('/'));
                fPath = tmpLine.right(tmpLine.size() - fProp.size() - 1);
                if (lines[k][4] == '4')
                {
                    // file type is xml
                    // This is a special case b/c the file name of the xml on disk will not be the same
                    // as on the device. mtpinterface will be giving us the name of the xml on disk, however,
                    // if we pass that path to delete, it will fail.
                    fProp = ".";
                    fName = "artour.xml";
                }
                else
                {
                    fInfo = new QFileInfo(fPath);
                    fName = fInfo->fileName();
                    msgbox->setText("File \"" + fName + "\" already exists.");
                    msgbox->setInformativeText("Would you like to overwrite it?");
                    owrite = msgbox->addButton("Overwrite", QMessageBox::AcceptRole);
                    keep = msgbox->addButton("Keep Current", QMessageBox::RejectRole);
                    msgbox->exec();
                    delete fInfo;
                }
                // Always overwrite xml without asking
                if (lines[k][4] == '4' || (QPushButton *)msgbox->clickedButton() == owrite)
                {
                    // need to delete the file, then copy it again
                    deleteFile(fProp, fName);
                    if (lines[k][4] != '4')
                    {
                        writeLine.push_back(sendFile(fPath, true, (PREFIX)QString((char)lines[k][4]).toInt(), fProp));
                    }
                    else
                    {
                        writeLine.push_back(QByteArray::number(FILE_XML));
                        writeLine.push_back(' ');
                        writeLine.push_back(tmpLine.toAscii());
                        writeLine.push_back('\n');
                    }
                    mtpCopyProc->write(writeLine);
                    break;
                }
                else if ((QPushButton *)msgbox->clickedButton() == keep)
                {
                }
                delete msgbox;
                break;
            case '8':
                // AUDIO_SCRIPT
                tmpLine = QString(lines[k].right(lines[k].size() - 4)).trimmed();
                fPath = tmpLine.left(tmpLine.indexOf('/'));
                fName = tmpLine.right(tmpLine.size()-fPath.size()-1);
                tmpProp = xml->getPropByName(fPath);
                if (tmpProp != 0)
                {
                    f = new QFile(fName);
                    f->open(QIODevice::ReadOnly | QIODevice::Text);
                    tmpProp->setAudMainScript(f->readAll(), true);
                }
                f->close();
                delete f;
                break;
            case '9':
                // COPY_ALL_COMPLETE
                xml->readXML(QDir::homePath() + "/.artour/artour.xml");
                loc->updatePaths();
                emit copyInProgress(false);
            }

            break;
        default:
            // default to info
            if (lines[k] != "")
            {
                emit logMessage("mtp: " + QString(lines[k]).trimmed(), INFO);
            }
            break;
        }
    }
}

void MTPInterface::pushData()
{
    if (!xml->buildXML())
    {
        // buildXML() failed for some reason, probably because minimap data is missing
        emit logMessage("Save to device cancelled (if you didn't cause this, yell at the programmer)", IMPORTANT);
        return;
    }
    //SyncStatusDialog *ssd = new SyncStatusDialog((QWidget *)this->parent());
    //connect(this, SIGNAL(logMessage(QString)), ssd, SLOT(addToLog(QString)));
    //ssd->show();
    std::vector<PropData *> props = xml->getAllProps();
    QByteArray writeLine;

    writeLine.push_back(QByteArray::number(COMMAND));
    writeLine.push_back(' ');
    writeLine.push_back(QByteArray::number(COPY));
    writeLine.push_back('\n');

    // xml
    writeLine.push_back(QByteArray::number(FILE_XML));
    writeLine.push_back(' ');
    writeLine.push_back(xml->getTmpXmlFile().toAscii());
    writeLine.push_back('\n');

    // minimap
    if (xml->getMinimap() != NULL && xml->getMinimap()->pic.size() > 0)
    {
        writeLine.push_back(QByteArray::number(PROP_NAME));
        writeLine.push_back(' ');
        writeLine.push_back("Minimap");
        writeLine.push_back('\n');
        writeLine.push_back(sendFile(xml->getMinimap()->pic[0]->first, true, FILE_PIC, "Minimap"));
        writeLine.push_back(QByteArray::number(COMMAND));
        writeLine.push_back(' ');
        writeLine.push_back(QByteArray::number(END_PROP));
        writeLine.push_back('\n');
        xml->getMinimap()->propStatus = PROP_SAVED;
    }

    // props
    for (unsigned int k = 0; k < props.size(); k ++)
    {
        writeLine.push_back(QByteArray::number(PROP_NAME));
        writeLine.push_back(' ');
        writeLine.push_back(props[k]->getPropName().toAscii());
        writeLine.push_back('\n');

        /*if (props[k]->getAudMain()->second == PROP_LOCAL_PATH && props[k]->mainAud != "")
        {
            writeLine.push_back(sendFile(props[k]->getAudMain()->first, props[k]->localPaths, FILE_AUD, props[k]->getPropName()));
        }
        if (props[k]->getAudMainScriptFile()->second == PROP_LOCAL_PATH && props[k]->getAudMainScript()->first != "")
        {
            writeLine.push_back(sendFile(props[k]->getAudMainScriptFile()->first, props[k]->localPaths, FILE_TXT, props[k]->getPropName()));
        }*/

        for (unsigned int j = 0; j < props[k]->aud.size(); j++)
        {
            //if (props[k]->aud[j]->second == PROP_LOCAL_PATH)
            //{
                writeLine.push_back(sendFile(props[k]->aud[j]->first, props[k]->localPaths, FILE_AUD, props[k]->getPropName()));
            //}
        }
        for (unsigned int j = 0; j < props[k]->vid.size(); j++)
        {
            //if (props[k]->vid[j]->second == PROP_LOCAL_PATH)
            //{
                writeLine.push_back(sendFile(props[k]->vid[j]->first, props[k]->localPaths, FILE_VID, props[k]->getPropName()));
            //}
        }
        for (unsigned int j = 0; j < props[k]->pic.size(); j++)
        {
            //if (props[k]->pic[j]->second == PROP_LOCAL_PATH)
            //{
                writeLine.push_back(sendFile(props[k]->pic[j]->first, props[k]->localPaths, FILE_PIC, props[k]->getPropName()));
            //}
        }
        writeLine.push_back(QByteArray::number(COMMAND));
        writeLine.push_back(' ');
        writeLine.push_back(QByteArray::number(END_PROP));
        writeLine.push_back('\n');

        // mark the prop as saved
        props[k]->propStatus = PROP_SAVED;
    }
    writeLine.push_back(QByteArray::number(COMMAND));
    writeLine.push_back(' ');
    writeLine.push_back(QByteArray::number(END_COPY));
    writeLine.push_back('\n');
    //emit logMessage(writeLine, INFO);
    mtpCopyProc->write(writeLine);
}

QByteArray MTPInterface::sendFile(QString path, bool localPath, PREFIX ft, QString propName)
{
    QByteArray line;
    line.push_back(QByteArray::number(ft));
    line.push_back(' ');
    line.push_back(propName.toAscii());
    line.push_back('/');
    QFileInfo finfo(path);
    if (!finfo.exists())
    {
        finfo.setFile(path + ".lnk");
        line.push_back(finfo.symLinkTarget().toAscii());
    }
    else
    {
        line.push_back(path.toAscii());
    }
    line.push_back('\n');
    return line;
}

void MTPInterface::copyXMLFromDevice()
{
    QByteArray line;
    line.push_back(QByteArray::number(COMMAND));
    line.push_back(' ');
    line.push_back(QByteArray::number(TMP_XML_PATH));
    line.push_back('\n');
    mtpCopyProc->write(line);
}

void MTPInterface::setPropAudioScriptFromDevice(PropData *prop)
{
    QByteArray line;
    QString scrFile = prop->audMainScriptFile;
    line.push_back(QByteArray::number(COMMAND));
    line.push_back(' ');
    line.push_back(QByteArray::number(AUDIO_SCRIPT));
    line.push_back(' ');
    line.push_back(prop->getPropName().toAscii());
    line.push_back('/');
    line.push_back(scrFile.remove(0, scrFile.lastIndexOf('/')+1).toAscii());
    line.push_back('\n');
    mtpCopyProc->write(line);
}

void MTPInterface::deleteProp(QString pName, bool update)
{
    QByteArray line;
    line.push_back(QByteArray::number(DELETE_PROP));
    line.push_back(' ');
    line.push_back(pName.toAscii());
    line.push_back('\n');
    mtpCopyProc->write(line);
    if (update)
        emit updateDevice();
}

void MTPInterface::deleteFile(QString pName, QString fName, bool update)
{
    QByteArray line;
    line.push_back(QByteArray::number(DELETE_FILE));
    line.push_back(' ');
    line.push_back(pName.toAscii());
    line.push_back('/');
    line.push_back(fName.toAscii());
    line.push_back('\n');
    mtpCopyProc->write(line);
    if (update)
        emit updateDevice();
}

void MTPInterface::renameProp(QString pNameOld, QString pNameNew, bool update)
{
    QByteArray line;
    line.push_back(QByteArray::number(RENAME_PROP));
    line.push_back(' ');
    line.push_back(pNameOld.toAscii());
    line.push_back('/');
    line.push_back(pNameNew.toAscii());
    line.push_back('\n');
    mtpCopyProc->write(line);
    if (update)
        emit updateDevice();
}

void MTPInterface::copyAllDataFromDevice(QString folderPath)
{
    QByteArray line;
    line.push_back(QByteArray::number(COPY_ALL_DATA));
    line.push_back(' ');
    line.push_back(folderPath.toAscii());
    line.push_back('\n');
    mtpCopyProc->write(line);
}
